/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ua.yandex.collections;

public class MyLinkedList implements MyList {

    private static class Node {

        Object value;
        Node next;
        Node prev;

        public Node(Object o) {
            value = o;
        }
    }

    private Node first;
    private Node last;
    private int length;

    public MyLinkedList() {
        first = null;
        last = null;
        length = 0;
    }

    public void addFirst(Object e) {
        Node n = new Node(e);
        if (first == null) {
            first = n;
            last = n;
        } else {
            first.prev = n;
            n.next = first;
            first = n;
        }
        length += 1;
    }

    public void addLast(Object e) {
        Node n = new Node(e);
        if (last == null) {
            first = n;
            last = n;
        } else {
            last.next = n;
            n.prev = last;
            last = n;
        }
        length += 1;
    }

    public Object getFirst() {
        return this.first.value;
    }

    public Object getLast() {
        return this.last.value;
    }

    public Object removeLast() {
        if (last == null) {
            return null;
        } else {
            length -= 1;
            last.prev.next = null;
            Node n = new Node(last.value);
            last = last.prev;
            return n.value;
        }

    }

    public Object removeFirst() {
        if (first == null) {
            return null;
        } else {
            length -= 1;
            first.next.prev = null;
            Node n = new Node(first.value);
            first = first.next;
            return n.value;
        }
    }

    //interface methods
    @Override
    public void add(Object e) {
        addLast(e);
    }

    @Override
    public void add(int index, Object e) {
        if ((index > length) || (index < 0)) {
            throw new OutOfRangeException();
        } else if (index == 0) {
            addFirst(e);
        } else if (index == length) {
            addLast(e);
        } else {
            Node n = first;
            for (int i = 1; i < index; i++) {
                n = n.next;
            }
            Node newNode = new Node(e);
            n.next.prev = newNode;
            newNode.next = n.next;
            n.next = newNode;
            newNode.prev = n;
            length += 1;
        }
    }

    @Override
    public void addAll(Object[] c) {
        if (c == null) {
            return;
        }
        for (Object o : c) {
            add(o);
        }
    }

    @Override
    public void addAll(int index, Object[] c) {
        if ((c == null) || (c.length == 0)) {
            return;
        }
        if ((index > length) || (index < 0)) {
            throw new OutOfRangeException();
        } else if (index == 0) {
            addFirst(c[0]);
            Node n = first;
            for (int i = 1; i < c.length; i++) {
                Node newNode = new Node(c[i]);
                n.next.prev = newNode;
                newNode.next = n.next;
                n.next = newNode;
                newNode.prev = n;
                length += 1;
                n = newNode;
            }
        } else if (index == length) {
            for (Object e : c) {
                addLast(e);
            }
        } else {
            Node n = first;
            for (int i = 1; i < index; i++) {
                n = n.next;
            }
            for (Object e : c) {
                Node newNode = new Node(e);
                n.next.prev = newNode;
                newNode.next = n.next;
                n.next = newNode;
                newNode.prev = n;
                length += 1;
                n = newNode;
            }
        }
    }

    @Override
    public Object get(int index) {
        if ((index > (length - 1)) || (index < 0)) {
            throw new OutOfRangeException();
        } else {
            Node n = first;
            for (int i = 1; i <= index; i++) {
                n = n.next;
            }
            return n.value;
        }
    }

    @Override
    public Object remove(int index) {
        if ((index < 0) || (index > (length - 1))) {
            throw new OutOfRangeException();
        } else {
            Node n = first;
            for (int i = 1; i <= index; i++) {
                n = n.next;
            }
            if (n.prev != null) {
                n.prev.next = n.next;
            } else {
                first = n.next;
            }
            if (n.next != null) {
                n.next.prev = n.prev;
            } else {
                last = n.prev;
            }
            length -= 1;
            return n.value;
        }
    }

    @Override
    public void set(int index, Object e) {
        if ((index < 0) || (index > (length - 1))) {
            throw new OutOfRangeException();
        } else {
            Node n = first;
            for (int i = 1; i <= index; i++) {
                n = n.next;
            }
            n.value = e;
        }
    }

    @Override
    public int indexOf(Object o) {
        if (isEmpty()) {
            return -1;
        }
        Node n = first;
        if (n.value == o) {
            return 0;
        }
        for (int i = 1; i < length; i++) {
            n = n.next;
            if (n.value == o) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int size() {
        return length;
    }

    @Override
    public void clear() {
        first = null;
        last = null;
        length = 0;
    }

    @Override
    public boolean isEmpty() {
        if (length == 0) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Object[] toArray() {
        Object[] arr = new Object[length];
        if (isEmpty()) {
            return null;
        } else {
            arr[0] = first.value;
            Node n = first;
            for (int i = 1; i < length; i++) {
                arr[i] = n.next.value;
                n = n.next;
            }
            return arr;
        }
    }

    @Override
    public String toString() {
        if (isEmpty()) {
            return "";
        } else {
            String s = first.value.toString();
            Node n = first;
            for (int i = 1; i < length; i++) {
                s = s.concat(n.next.value.toString());
                n = n.next;
            }
            return s;
        }
    }

}
